#include "stdafx.h"
#include "resource.h"
#include "efx.h"
#include "Timer.h"
#include "Blurer.h"
#include "ImageQuad.h"

// efx includes

#include "TitleSequence.h"
#include "Cubes.h"
#include "Accelerator.h"
#include "Mirrors.h"
#include "Projector.h"
#include "BlobsEfx.h"
#include "Construct.h"
#include "Octopus.h"
#include "City.h"
#include "FinishSequence.h"

// bass
#include "bass.h"

//#define	_NOSOUND

#define MAX_LOADSTRING	100
#define	LOGFAIL( s )	logstream << "failed" << endl << "\t\t!exception caught: " << (s) << endl
#define	LOGEFX( s )		logstream << "\t\t·" << (s) << " efx initialization: "

#define	XRES			640
#define	YRES			480


// Global Variables:
HINSTANCE			hInst;													// current instance
HWND				hWnd;
TCHAR				szTitle[MAX_LOADSTRING];								// The title bar text
TCHAR				szWindowClass[MAX_LOADSTRING];							// The title bar text

BOOL				g_bEsc = FALSE;

// DX objects

PDIRECT3D8			g_pD3D = NULL;
PDIRECT3DDEVICE8	g_pDevice = NULL;
DWORD				g_pFrames = 0;

// Foward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
HRESULT				InitD3D( HWND );
HRESULT				CleanUp();


int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{ 	
	MSG msg;

#ifndef _DEBUG
	ShowCursor( FALSE );
#endif

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_ASTATE, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	CTimer*					timer = new CMultimediaTimer();
	CBlurer*				blur = NULL;		
	CImageQuad*				piqEfx = NULL;

	// efx objects

	CTitleSequence*			title = NULL;
	CCubes*					cubes = NULL;
	CAccelerator*			accel = NULL;
	CMirrors*				mirrors = NULL;
	CProjector*				projector = NULL;
	CBlobsEfx*				blobs = NULL;
	CConstruct*				construct = NULL;
	COctopus*				octopus = NULL;
	CCity*					city = NULL;
	CFinishSequence*		fin= NULL;

	HSTREAM					track;

	FLOAT					fTime;

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow)) 
		return FALSE;

	logstream.open( "log.txt" );

	logstream << "\tzone51 demo: [altered state/industrial love] logfile" << endl;
	logstream << "\tcompiled: " << __TIMESTAMP__ << endl << endl;
	logstream << "\t=[sequence][init]===================================" << endl << endl;

#ifndef _NOSOUND
	logstream << "\t\t·bass initialization: ";

	if( !BASS_Init( -1, 44100, 0, 0 ) )
	{
		logstream << "failed" << endl;
		return FALSE;
	}

	logstream << "succeeded" << endl;

	logstream << "\t\t·track loading: ";
	if( ! ( track = BASS_StreamCreateFile( FALSE, "track.mp3", 0, 0, 0 ) ) )
	{
		logstream << "failed" << endl;
		return FALSE;
	}

	logstream << "succeeded" << endl;

	BASS_Start();
	BASS_SetVolume( 80 );
#endif

	logstream << "\t\t·system initialization: ";

	if( FAILED( InitD3D( hWnd ) ) )	
	{	
		DestroyWindow( hWnd );
		return FALSE;
	}	

	logstream << "succeeded" << endl;

	logstream << "\t\t·texture subsystem initialization: ";
	InitializeTextureSystem( g_pDevice );
	SetTexturePath( "data\\textures\\" );
	logstream << "succeeded" << endl;

	logstream << "\t\t·system objects initialization: ";

	try
	{	
		blur = new CBlurer( g_pDevice, TRUE, 512, 512 );
		piqEfx = new CImageQuad( g_pDevice );
		timer->Init();
		logstream << "succeeded" << endl;

		LOGEFX( "title sequence" );
		title = new CTitleSequence( g_pDevice );
		logstream << "succeeded" << endl;

		LOGEFX( "cubes" );
		cubes = new CCubes( g_pDevice );
		logstream << "succeeded" << endl;

		LOGEFX( "accelerator" );
		accel = new CAccelerator( g_pDevice );
		logstream << "succeeded" << endl;

		LOGEFX( "mirrors" );
		mirrors = new CMirrors( g_pDevice );
		logstream << "succeeded" << endl;

		LOGEFX( "projector" );
		projector = new CProjector( g_pDevice );
		logstream << "succeeded" << endl;

		LOGEFX( "blobs" );
		blobs = new CBlobsEfx( g_pDevice );
		logstream << "succeeded" << endl;

		LOGEFX( "construct" );
		construct = new CConstruct( g_pDevice );
		logstream << "succeeded" << endl;

		LOGEFX( "octopus" );
		octopus = new COctopus( g_pDevice );
		logstream << "succeeded" << endl;

		LOGEFX( "city" );
		city = new CCity( g_pDevice );
		logstream << "succeeded" << endl;

		LOGEFX( "finish seq" );
		fin = new CFinishSequence( g_pDevice );
		logstream << "succeeded" << endl;

	}	
	catch( CSystemException e )
	{		
		LOGFAIL( e.GetMessage() );		
				
		logstream << "\t\t·squence finished" << endl << endl;
		logstream << "\t=[sequence][exit]===================================" << endl << endl;
		logstream << "\t\t·exit reason: init failed" << endl;		

#ifndef _NOSOUND
		logstream << "\t\t·closing bass sound system: ";

		BASS_Stop();
		BASS_StreamFree( track );
		BASS_Free();
		logstream << "succeeded" << endl;
#endif

		logstream << "\t\t·freeing fx objects: ";
		
		SAFE_DELETE( timer );
		SAFE_DELETE( blur );
		SAFE_DELETE( piqEfx );
		SAFE_DELETE( title );
		SAFE_DELETE( accel );
		SAFE_DELETE( cubes );
		SAFE_DELETE( mirrors );
		SAFE_DELETE( projector );
		SAFE_DELETE( blobs );
		SAFE_DELETE( construct );
		SAFE_DELETE( octopus );
		SAFE_DELETE( city );

		logstream << "succeeded" << endl;

		logstream << "\t\t·closing texture subsystem: ";

		CloseTextureSystem();

		logstream << "succeeded" << endl;
		
		logstream << "\t\t·freeing dx objects: ";

		CleanUp();										// succeeded up a direct3d objects	

		logstream << "succeeded" << endl;
		logstream << "\t\t·sequence finished" << endl << endl;

		return FALSE;
	}	

	logstream << "\t\t·squence finished" << endl << endl;
		
	g_pDevice->Clear( 0, NULL, D3DCLEAR_TARGET, 0x0, 1.0f, 0 );		
	timer->Start();

#ifndef _NOSOUND
	BASS_StreamPlay( track, FALSE, 0 );
#endif
	
	do
	{
		if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
		{			
			TranslateMessage(&msg);
			DispatchMessage(&msg);			
		}
		else
		{
			fTime = timer->GetCurrentSec();

			if( fTime < 20.0f )
			{			
				blur->SetBlurFactor( 0.5f );				
				title->UpdateFrame( fTime );

				blur->BeginScene();
				title->RenderEfx();
				blur->EndScene();
			}
			else if ( fTime < 22.0f )
			{
				title->UpdateFrame( fTime );

				blur->BeginScene();
				title->RenderEfx();
				blur->EndScene();
				
				g_pDevice->Clear( 0, NULL, D3DCLEAR_STENCIL, 0, 0.0f, 0 );

				g_pDevice->SetRenderState( D3DRS_STENCILENABLE, TRUE );
				g_pDevice->SetRenderState( D3DRS_STENCILREF, 0x1 );
				g_pDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE );

				g_pDevice->SetTexture( 0, GetTexture( "back.bmp" ) );	
				g_pDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );				

				DWORD		dwAlpha = 255 - (DWORD)( ( fTime - 20.0f )*128.0f );

				g_pDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
				g_pDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
				g_pDevice->SetRenderState( D3DRS_ALPHAREF, dwAlpha );

				piqEfx->Render( g_pDevice );

				g_pDevice->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );
				g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
	
				g_pDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );	

				g_pDevice->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_EQUAL );
				g_pDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP );
				
				cubes->UpdateFrame( fTime - 20.0f );
				g_pDevice->BeginScene();
				cubes->RenderEfx();
				g_pDevice->EndScene();

				g_pDevice->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_ALWAYS );
				g_pDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE );
			}
			else if( fTime < 40.0f )
			{
				g_pDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, 0xffffff, 1.0f, 0x0 );

				cubes->UpdateFrame( fTime - 20.0f );
				g_pDevice->BeginScene();
				cubes->RenderEfx();
				g_pDevice->EndScene();
			}
			else if( fTime < 65.5f )
			{
				accel->UpdateFrame( fTime - 40.0f );				
				accel->RenderEfx();				
			}
			else if( fTime < 88.0f )
			{			
				mirrors->UpdateFrame( fTime - 65.5f );
				g_pDevice->BeginScene();
				mirrors->RenderEfx();
				g_pDevice->EndScene();
			}
			else if( fTime < 112.0f )
			{				
				projector->UpdateFrame( fTime - 88.0f );
				g_pDevice->BeginScene();
				projector->RenderEfx();
				g_pDevice->EndScene();
			}
			else if( fTime < 139.0f )
			{
				blur->SetBlurFactor( 0.5f );
				blobs->UpdateFrame( fTime - 112.0f );
				blur->BeginScene();
				blobs->RenderEfx();
				blur->EndScene();
				
				g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
				g_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
				g_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );

				g_pDevice->SetTexture( 0, NULL );				

				if( fTime < 114.0f )
				{
					piqEfx->SetAlpha( (DWORD)(CLAMPALPHA( ( 114.0f - fTime )*128.0f ) ) );
					piqEfx->Render( g_pDevice );					
				}
				else if( fTime > 137.0f )
				{
					piqEfx->SetAlpha( (DWORD)(CLAMPALPHA( ( fTime - 137.0f )*140.0f ) ) );
					piqEfx->Render( g_pDevice );
				}
			}
			else if( fTime < 165.0f )
			{
				construct->UpdateFrame( fTime - 139.0f );
				g_pDevice->BeginScene();
				construct->RenderEfx();
				g_pDevice->EndScene();
			}
			else if( fTime < 185.0f )
			{
				octopus->UpdateFrame( fTime - 165.0f );
				g_pDevice->BeginScene();
				octopus->RenderEfx();
				g_pDevice->EndScene();
			}
			else if( fTime < 205.0f )			
			{
				blur->SetBlurFactor( 0.3f );
				city->UpdateFrame( fTime - 185.0f );
				blur->BeginScene();
				city->RenderEfx();
				blur->EndScene();

				g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
				g_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
				g_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );

				g_pDevice->SetTexture( 0, NULL );				

				if( fTime < 187.0f )
				{
					piqEfx->SetAlpha( (DWORD)(CLAMPALPHA( ( 187.0f - fTime )*128.0f ) ) );
					piqEfx->Render( g_pDevice );					
				}
				else if( fTime > 203.0f )
				{
					piqEfx->SetAlpha( (DWORD)(CLAMPALPHA( ( fTime - 203.0f )*140.0f ) ) );
					piqEfx->Render( g_pDevice );
				}

				g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
			}
			else if( fTime < 225.0f )
			{
				fin->UpdateFrame( fTime - 206.0f );
				g_pDevice->BeginScene();
				fin->RenderEfx();
				g_pDevice->EndScene();

				if( fTime > 223.0f )
				{
					piqEfx->SetColor( 0x0 );
					piqEfx->SetAlpha( (DWORD)CLAMPALPHA( (fTime - 223.0f)*128.0f ) );

					g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
					g_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
					g_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

					g_pDevice->SetTexture( 0, NULL );

					piqEfx->Render( g_pDevice );
				}
			}
			else
				DestroyWindow( hWnd );

			g_pFrames++;

			g_pDevice->Present( NULL, NULL, NULL, NULL );
		}

	}while ( msg.message != WM_QUIT );

	logstream << "\t=[sequence][exit]===================================" << endl << endl;
	logstream << "\t\t·exit reason: ";

	if( g_bEsc )	
		logstream << "esc pressed" << endl;
	else
		logstream << "demo ended" << endl;	

#ifndef _NOSOUND
	logstream << "\t\t·closing bass sound system: ";

	BASS_Stop();
	BASS_StreamFree( track );
	BASS_Free();
	logstream << "succeeded" << endl;
#endif
	
	FLOAT					fFPS = (FLOAT)g_pFrames/timer->GetCurrentSec();

	g_pDevice->SetStreamSource( 0, NULL, 0 );
	g_pDevice->SetIndices( NULL, 0 );
	g_pDevice->SetTexture( 0, NULL );
	g_pDevice->SetTexture( 0, NULL );

	logstream << "\t\t·freeing fx objects: ";
	
	SAFE_DELETE( timer );
	SAFE_DELETE( blur );
	SAFE_DELETE( piqEfx );
	SAFE_DELETE( title );
	SAFE_DELETE( accel );
	SAFE_DELETE( cubes );
	SAFE_DELETE( mirrors );
	SAFE_DELETE( projector );
	SAFE_DELETE( blobs );
	SAFE_DELETE( construct );
	SAFE_DELETE( octopus );
	SAFE_DELETE( city );

	logstream << "succeeded" << endl;

	logstream << "\t\t·closing texture subsystem: ";

	CloseTextureSystem();

	logstream << "succeeded" << endl;
		
	logstream << "\t\t·freeing dx objects: ";

	CleanUp();										// succeeded up a direct3d objects	

	logstream << "succeeded" << endl;
	logstream << "\t\t·sequence finished" << endl << endl;
	
	logstream << endl << "\t\t·average fps: " << fFPS;
	logstream.close();

#ifndef _DEBUG
	ShowCursor( TRUE );
#endif
	
	return msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage is only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_ENABLE;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, (LPCTSTR)IDI_ASTATE);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= NULL;
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{	
	hInst = hInstance; // Store instance handle in our global variable   

#ifdef _DEBUG

	RECT						rc = { 0, 0, 640, 480 };
	INT							cWidth, cHeight;
	INT							cWindowWidth, cWindowHeight;

	AdjustWindowRectEx( &rc, WS_POPUP|WS_CAPTION|WS_SYSMENU, FALSE, WS_EX_TOOLWINDOW|WS_EX_STATICEDGE );

	cWidth = GetSystemMetrics( SM_CXSCREEN );
	cHeight = GetSystemMetrics( SM_CYSCREEN );	
	cWindowWidth = rc.right - rc.left;
	cWindowHeight = rc.bottom - rc.top;

	hWnd = CreateWindowEx( WS_EX_TOOLWINDOW|WS_EX_STATICEDGE, szWindowClass, szTitle, WS_POPUP|WS_CAPTION|WS_SYSMENU,
					       (cWidth - cWindowWidth)/2, (cHeight - cWindowHeight)/2, cWindowWidth, cWindowHeight, NULL, NULL, hInstance, NULL);
#else
	hWnd = CreateWindowEx( WS_EX_TOOLWINDOW, szWindowClass, szTitle, WS_POPUP,
						   0, 0, 0, 0, NULL, NULL, hInstance, NULL);
#endif

   if (!hWnd)   
      return FALSE;   

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{	
	switch (message) 
	{		
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		case WM_KEYDOWN:
			if( wParam == VK_ESCAPE )
			{		
				g_bEsc = TRUE;
#ifndef _NOSOUND
				BASS_Stop();
#endif
				DestroyWindow( hWnd );			
			}			
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

HRESULT InitD3D( HWND hWnd )
{	
	// create directGraphic interface
	if( !( g_pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) )
	{			
		LOGFAIL( "unalbe to create d3d" );
		return E_FAIL;
	}	

	D3DPRESENT_PARAMETERS	d3dpp;

	ZeroMemory( &d3dpp, sizeof(D3DPRESENT_PARAMETERS) );

#ifdef _DEBUG

	D3DDISPLAYMODE			d3ddm;

	g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm );

	d3dpp.Windowed			= TRUE;
	d3dpp.SwapEffect		= D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat	= d3ddm.Format;
#else
	d3dpp.Windowed			= FALSE;
	d3dpp.SwapEffect		= D3DSWAPEFFECT_FLIP;
	d3dpp.BackBufferFormat	= D3DFMT_A8R8G8B8;
#endif

	d3dpp.BackBufferCount	= 3;
	d3dpp.BackBufferWidth	= XRES;
	d3dpp.BackBufferHeight	= YRES;	
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
	
	if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, 
									  D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pDevice )	) )
	{
		LOGFAIL( "unable to create device" );
		return E_FAIL;
	}				
	
	g_pDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
	g_pDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
	g_pDevice->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR );
	g_pDevice->SetTextureStageState( 1, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
	g_pDevice->SetTextureStageState( 1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
	g_pDevice->SetTextureStageState( 1, D3DTSS_MIPFILTER, D3DTEXF_LINEAR );		
	
	return S_OK;				
}

HRESULT CleanUp()
{		
	if( g_pDevice )
		g_pDevice->Release();

	if( g_pD3D )
		g_pD3D->Release();	

	return S_OK;
}

